home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Blender 2.49b / blender-2.49b-windows.exe / $_4_ / .blender / scripts / armature_symmetry.py < prev    next >
Text File  |  2009-08-31  |  10KB  |  326 lines

  1. #!BPY
  2.  
  3. """
  4. Name: 'Armature Symmetry'
  5. Blender: 242
  6. Group: 'Armature'
  7. Tooltip: 'Make an Armature symmetrical'
  8. """
  9.  
  10. __author__ = "Campbell Barton"
  11. __url__ = ("blender", "blenderartist")
  12. __version__ = "1.0 2006-7-26"
  13.  
  14. __doc__ = """\
  15. This script creates perfectly symmetrical armatures,
  16. based on the best fit when comparing the mirrored locations of 2 bones.
  17. Hidden bones are ignored, and optionally only operate on selected bones.
  18. """
  19.  
  20. # ***** BEGIN GPL LICENSE BLOCK *****
  21. #
  22. # Script copyright (C) Campbell J Barton 2006
  23. #
  24. # This program is free software; you can redistribute it and/or
  25. # modify it under the terms of the GNU General Public License
  26. # as published by the Free Software Foundation; either version 2
  27. # of the License, or (at your option) any later version.
  28. #
  29. # This program is distributed in the hope that it will be useful,
  30. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  31. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  32. # GNU General Public License for more details.
  33. #
  34. # You should have received a copy of the GNU General Public License
  35. # along with this program; if not, write to the Free Software Foundation,
  36. # Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  37. #
  38. # ***** END GPL LICENCE BLOCK *****
  39. # --------------------------------------------------------------------------
  40.  
  41. import Blender
  42. import bpy
  43. Vector= Blender.Mathutils.Vector
  44.  
  45.  
  46. def VecXFlip(vec):
  47.     '''
  48.     Return a copy of this vector x flipped.
  49.     '''
  50.     x,y,z= vec
  51.     return Vector(-x,y,z)
  52.  
  53. def editbone_mirror_diff(editbone1, editbone2):
  54.     '''
  55.     X Mirror bone compare
  56.     return a float representing the difference between the 2 bones
  57.     the smaller the better the match
  58.     '''
  59.     h1= editbone1.head
  60.     h2= editbone2.head
  61.     
  62.     t1= editbone1.tail
  63.     t2= editbone2.tail
  64.     
  65.     # Mirror bone2's location
  66.     h2= VecXFlip(h2)
  67.     t2= VecXFlip(t2)
  68.     
  69.     #return (h1-h2).length + (t1-t2).length # returns the length only
  70.     
  71.     # For this function its easier to return the bones also
  72.     return ((h1-h2).length + (t1-t2).length)/2, editbone1, editbone2
  73.  
  74. def editbone_mirror_merge(editbone1, editbone2, PREF_MODE_L2R, PREF_MODE_R2L):
  75.     '''
  76.     Merge these 2 bones to their mirrored locations
  77.     '''
  78.     h1= editbone1.head
  79.     h2= editbone2.head
  80.     
  81.     t1= editbone1.tail
  82.     t2= editbone2.tail
  83.     
  84.     if PREF_MODE_L2R and PREF_MODE_R2L:
  85.         # Median, flip bone2's locations and average, then apply to editbone1, flip and apply to editbone2
  86.         h2_f= VecXFlip(h2)
  87.         t2_f= VecXFlip(t2)
  88.         
  89.         h_med= (h1+h2_f)*0.5 # middle between t1 and flipped t2
  90.         t_med= (t1+t2_f)*0.5 # middle between h1 and flipped h2
  91.         
  92.         # Apply the median to editbone1
  93.         editbone1.head= h_med
  94.         editbone1.tail= t_med
  95.         
  96.         # Flip in place for editbone2
  97.         h_med.x= -h_med.x
  98.         t_med.x= -t_med.x
  99.         
  100.         # Apply the median to editbone2
  101.         editbone2.head= h_med
  102.         editbone2.tail= t_med
  103.         
  104.         # Average the roll, this might need some logical work, but looks good for now.
  105.         r1= editbone1.roll
  106.         r2= -editbone2.roll
  107.         # print 'rolls are', r1,r2
  108.         r_med= (r1+r2)/2
  109.         # print 'new roll is', r_med
  110.         editbone1.roll= r_med
  111.         editbone2.roll= -r_med # mirror roll
  112.     
  113.     else: # Copy from 1 side to another
  114.         
  115.         # Crafty function we can use so L>R and R>L can use the same code
  116.         def IS_XMIRROR_SOURCE(xval):
  117.             '''Source means is this the value we want to copy from'''
  118.             
  119.             if PREF_MODE_L2R:
  120.                 if xval<0:    return True
  121.                 else:        return False
  122.             else: # PREF_MODE_R2L
  123.                 if xval<0:    return False
  124.                 else:        return True
  125.         
  126.         if IS_XMIRROR_SOURCE( h1.x ):# head bone 1s negative, so copy it to h2
  127.             editbone2.head= VecXFlip(h1)
  128.         else:
  129.             '''
  130.             assume h2.x<0 - not a big deal if were wrong,
  131.             its unlikely to ever happen because the bones would both be on the same side.
  132.             '''
  133.             
  134.             # head bone 2s negative, so copy it to h1
  135.             editbone1.head= VecXFlip(h2)
  136.         
  137.         # Same as above for tail
  138.         if IS_XMIRROR_SOURCE(t1.x):
  139.             editbone2.tail= VecXFlip(t1)
  140.         else:
  141.             editbone1.tail= VecXFlip(t2)
  142.         
  143.         # Copy roll from 1 bone to another, use the head's location to decide which side it's on.
  144.         if IS_XMIRROR_SOURCE(editbone1.head):
  145.             editbone2.roll= -editbone1.roll
  146.         else:
  147.             editbone1.roll= -editbone2.roll
  148.         
  149.  
  150. def armature_symetry(\
  151.     arm_ob,\
  152.     PREF_MAX_DIST,\
  153.     PREF_XMID_SNAP,\
  154.     PREF_XZERO_THRESH,\
  155.     PREF_MODE_L2R,\
  156.     PREF_MODE_R2L,\
  157.     PREF_SEL_ONLY):
  158.     
  159.     '''
  160.     Main function that does all the work,
  161.     return the number of 
  162.     '''
  163.     arm_data= arm_ob.data
  164.     arm_data.makeEditable()
  165.     
  166.     # Get the bones
  167.     bones= []
  168.     HIDDEN_EDIT= Blender.Armature.HIDDEN_EDIT
  169.     BONE_SELECTED= Blender.Armature.BONE_SELECTED
  170.     
  171.     if PREF_SEL_ONLY:
  172.         for eb in arm_data.bones.values():
  173.             options= eb.options
  174.             if HIDDEN_EDIT not in options and BONE_SELECTED in options:
  175.                 bones.append(eb)
  176.     else:
  177.         # All non hidden bones
  178.         for eb in arm_data.bones.values():
  179.             options= eb.options
  180.             if HIDDEN_EDIT not in options:
  181.                 bones.append(eb)
  182.     
  183.     del HIDDEN_EDIT # remove temp variables
  184.     del BONE_SELECTED
  185.     
  186.     # Store the numder of bones we have modified for a message
  187.     tot_editbones= len(bones)
  188.     tot_editbones_modified= 0
  189.     
  190.     if PREF_XMID_SNAP:
  191.         # Remove bones that are in the middle (X Zero)
  192.         # reverse loop so we can remove items in the list.
  193.         for eb_idx in xrange(len(bones)-1, -1, -1):
  194.             edit_bone= bones[eb_idx]
  195.             if abs(edit_bone.head.x) + abs(edit_bone.tail.x) <= PREF_XZERO_THRESH/2:
  196.                 
  197.                 # This is a center bone, clamp and remove from the bone list so we dont use again.
  198.                 if edit_bone.tail.x or edit_bone.head.x:
  199.                     tot_editbones_modified += 1
  200.                     
  201.                 edit_bone.tail.x= edit_bone.head.x= 0
  202.                 del bones[eb_idx]
  203.                 
  204.                 
  205.     
  206.     
  207.     bone_comparisons= []
  208.     
  209.     # Compare every bone with every other bone, shouldn't be too slow.
  210.     # These 2 "for" loops only compare once
  211.     for eb_idx_a in xrange(len(bones)-1, -1, -1):
  212.         edit_bone_a= bones[eb_idx_a]
  213.         for eb_idx_b in xrange(eb_idx_a-1, -1, -1):
  214.             edit_bone_b= bones[eb_idx_b]
  215.             # Error float the first value from editbone_mirror_diff() so we can sort the resulting list.
  216.             bone_comparisons.append(editbone_mirror_diff(edit_bone_a, edit_bone_b))
  217.     
  218.     
  219.     bone_comparisons.sort() # best matches first
  220.     
  221.     # Make a dict() of bone names that have been used so we dont mirror more then once
  222.     bone_mirrored= {}
  223.     
  224.     for error, editbone1, editbone2 in bone_comparisons:
  225.         # print 'Trying to merge at error %.3f' % error
  226.         if error > PREF_MAX_DIST:
  227.             # print 'breaking, max error limit reached PREF_MAX_DIST: %.3f' % PREF_MAX_DIST
  228.             break
  229.         
  230.         if not bone_mirrored.has_key(editbone1.name) and not bone_mirrored.has_key(editbone2.name):
  231.             # Were not used, execute the mirror
  232.             editbone_mirror_merge(editbone1, editbone2, PREF_MODE_L2R, PREF_MODE_R2L)
  233.             # print 'Merging bones'
  234.             
  235.             # Add ourselves so we aren't touched again
  236.             bone_mirrored[editbone1.name] = None # dummy value, would use sets in python 2.4
  237.             bone_mirrored[editbone2.name] = None
  238.             
  239.             # If both options are enabled, then we have changed 2 bones
  240.             tot_editbones_modified+= PREF_MODE_L2R + PREF_MODE_R2L
  241.             
  242.     arm_data.update() # get out of armature editmode
  243.     return tot_editbones, tot_editbones_modified
  244.  
  245.     
  246. def main():
  247.     '''
  248.     User interface function that gets the options and calls armature_symetry()
  249.     '''
  250.     
  251.     scn= bpy.data.scenes.active
  252.     arm_ob= scn.objects.active
  253.     
  254.     if not arm_ob or arm_ob.type!='Armature':
  255.         Blender.Draw.PupMenu('No Armature object selected.')
  256.         return
  257.     
  258.     # Cant be in editmode for armature.makeEditable()
  259.     is_editmode= Blender.Window.EditMode()
  260.     if is_editmode: Blender.Window.EditMode(0)
  261.     Draw= Blender.Draw
  262.     
  263.     # Defaults for the user input
  264.     PREF_XMID_SNAP= Draw.Create(1)
  265.     PREF_MAX_DIST= Draw.Create(0.4)
  266.     PREF_XZERO_THRESH= Draw.Create(0.02)
  267.     
  268.     PREF_MODE_L2R= Draw.Create(1)
  269.     PREF_MODE_R2L= Draw.Create(0)
  270.     PREF_SEL_ONLY= Draw.Create(1)
  271.     
  272.     pup_block = [\
  273.     'Left (-), Right (+)',\
  274.     ('Left > Right', PREF_MODE_L2R, 'Copy from the Left to Right of the mesh. Enable Both for a mid loc.'),\
  275.     ('Right > Left', PREF_MODE_R2L, 'Copy from the Right to Left of the mesh. Enable Both for a mid loc.'),\
  276.     '',\
  277.     ('MaxDist:', PREF_MAX_DIST, 0.0, 4.0, 'Maximum difference in mirror bones to match up pairs.'),\
  278.     ('XZero limit:', PREF_XZERO_THRESH, 0.0, 2.0, 'Tolerance for locking bones into the middle (X/zero).'),\
  279.     ('XMidSnap Bones', PREF_XMID_SNAP, 'Snap middle verts to X Zero (uses XZero limit)'),\
  280.     ('Selected Only', PREF_SEL_ONLY, 'Only xmirror selected bones.'),\
  281.     ]
  282.     
  283.     # Popup, exit if the user doesn't click OK
  284.     if not Draw.PupBlock("X Mirror mesh tool", pup_block):
  285.         return    
  286.     
  287.     # Replace the variables with their button values.
  288.     PREF_XMID_SNAP= PREF_XMID_SNAP.val
  289.     PREF_MAX_DIST= PREF_MAX_DIST.val
  290.     PREF_MODE_L2R= PREF_MODE_L2R.val
  291.     PREF_MODE_R2L= PREF_MODE_R2L.val
  292.     PREF_XZERO_THRESH= PREF_XZERO_THRESH.val
  293.     PREF_SEL_ONLY= PREF_SEL_ONLY.val
  294.     
  295.     # If both are off assume mid-point and enable both
  296.     if not PREF_MODE_R2L and not PREF_MODE_L2R:
  297.         PREF_MODE_R2L= PREF_MODE_L2R= True
  298.     
  299.     
  300.     tot_editbones, tot_editbones_modified = armature_symetry(\
  301.       arm_ob,\
  302.       PREF_MAX_DIST,\
  303.       PREF_XMID_SNAP,\
  304.       PREF_XZERO_THRESH,\
  305.       PREF_MODE_L2R,\
  306.       PREF_MODE_R2L,\
  307.       PREF_SEL_ONLY)
  308.     
  309.     if is_editmode: Blender.Window.EditMode(1)
  310.     
  311.     # Redraw all views before popup
  312.     Blender.Window.RedrawAll()
  313.     
  314.     # Print results
  315.     if PREF_SEL_ONLY:
  316.         msg= 'moved %i bones of %i selected' % (tot_editbones_modified, tot_editbones)
  317.     else:
  318.         msg= 'moved %i bones of %i visible' % (tot_editbones_modified, tot_editbones)
  319.     
  320.     
  321.     Blender.Draw.PupMenu(msg)
  322.     
  323. # Check for __main__ so this function can be imported by other scripts without running the script.
  324. if __name__=='__main__':
  325.     main()
  326.